{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "UP2Th8B0LLey"
   },
   "source": [
    "# Practical 2: Introduction to Python (2)\n",
    "\n",
    "Upon completion of this session you should be able to:\n",
    "\n",
    "   - understand the basic syntax of Python functions, classes and regular expressions\n",
    "   - understand Python file handling\n",
    "   - create basic Python programs using the concepts\n",
    "\n",
    "\n",
    "---\n",
    "- Materials in this module include resources collected from various open-source online repositories.\n",
    "- Jupyter source file can be downloaded from clouddeakin SIT384 > weekly resources or https://github.com/gaoshangdeakin/SIT384-Jupyter\n",
    "- If you found any issue/bug for this document, please submit an issue at [https://github.com/gaoshangdeakin/SIT384/issues](https://github.com/gaoshangdeakin/SIT384/issues)\n",
    "\n",
    "\n",
    "---\n",
    "\n",
    "\n",
    "## Background\n",
    "\n",
    "In week 1 prac, we introduced basic Python syntax. This week we will learn more about Python functions, classes, regular expressions, and file handling. \n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "V0vSWcazLLe3"
   },
   "source": [
    "## Content\n",
    "\n",
    "### 1. Python functions\n",
    "\n",
    "### 2. Python lambda\n",
    "\n",
    "### 3. Python classes/objects\n",
    "\n",
    "### 4. Python iterators\n",
    "\n",
    "### 5. Python JSON\n",
    "\n",
    "### 6. Python RegEx\n",
    "\n",
    "### 7. Python file handling\n",
    "\n",
    "\n",
    "## Tasks\n",
    "\n",
    "## Summary\n",
    "\n",
    "## Reference\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "DAeickflLLe7"
   },
   "source": [
    "### Python functions\n",
    "\n",
    "A function is a block of code which only runs when it is called. You can pass parameters to it and get data returned from it.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#to create a function, we use def keyword\n",
    "def myfunction():\n",
    "  print(\"Hello from a function\") # note the indentation\n",
    "\n",
    "#call a function\n",
    "myfunction()\n",
    "\n",
    "#pass parameters inside the parentheses, separated with comma\n",
    "def mynamefunction(name):\n",
    "  print(name)\n",
    "mynamefunction(\"John\")\n",
    "\n",
    "#we can even set default parameter value. If no parameter passed when we call the function, the default one is used.\n",
    "def mycountryfunction(country = \"Australia\"):\n",
    "  print(\"I live in \" + country)\n",
    "mycountryfunction(\"India\")\n",
    "mycountryfunction() # use default \"Australia\"\n",
    "\n",
    "#return a value using return statement\n",
    "def mydoublefunction(x):\n",
    "  return 2 * x\n",
    "print(mydoublefunction(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "C6uxTijTLLe_"
   },
   "source": [
    "<a id = \"cell_start\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python also accepts function recursion, which means a defined function can call itself. You should be very careful with recursion as it can be quite easy to slip into writing a function which never terminates, or one that uses excess amounts of memory or processor power. However, when written correctly recursion can be a very efficient. Make sure the recursion ends when a condition is satisfied."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#create a function which calls iteself. k variable decrements (-1) every time.\n",
    "#the recursion ends when k is NOT greater than 0\n",
    "def tri_recursion(k):\n",
    "  if(k>0):\n",
    "    result = k+tri_recursion(k-1)\n",
    "    print(result)\n",
    "  else:\n",
    "    result = 0\n",
    "  return result\n",
    "\n",
    "print(\"\\n\\nRecursion Example Results\")\n",
    "tri_recursion(3)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Python lambda"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A lambda function is a small anonymous function. It can take any number of arguments, but can only have one expression. Use lambda functions when an anonymous function is required for a short period of time.\n",
    "Syntax:\n",
    "lambda arguments : expression The expression is executed and the result is returned."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#a lambda function that doubles the number passed in as an argument, and print the result:\n",
    "x = lambda a : a *2\n",
    "print(x(5))\n",
    "\n",
    "#multiplies argument a with argument b and print the result\n",
    "x = lambda a, b : a * b\n",
    "print(x(2, 3)) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The power of lambda is better shown when you use them as an anonymous function inside another function. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#you have a function definition that takes one argument,\n",
    "#and that argument will be multiplied with an unknown number.\n",
    "def myfunc(n):\n",
    "  return lambda a : a * n\n",
    "\n",
    "#Use that function definition to make a function that always doubles the number you send in\n",
    "mydoubler = myfunc(2) # set a to 2\n",
    "print(mydoubler(3)) # pass 3 to n\n",
    "\n",
    "#use the same function definition to make a function that always triples the number you send in\n",
    "mytripler = myfunc(3)\n",
    "print(mytripler(11)) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Python classes/objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python is an object oriented programming language. Almost everything in Python is an object, with its properties and methods. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a class using keywrod class.\n",
    "class MyClass:\n",
    "   x = 5\n",
    "\n",
    "obj1 = MyClass()\n",
    "print(obj1.x) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All classes have a function called __init__(), which is always executed when the class is being initiated. Use the __init__() function to assign values to object properties, or other operations that are necessary to do when the object is being created. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a class named Person, use the __init__() function to assign values for name and age:\n",
    "#The self parameter is a reference to the class instance itself,\n",
    "#and is used to access variables that belongs to the class.\n",
    "#It does not have to be named self , you can call it whatever you like,\n",
    "#but it has to be the first parameter of any function in the class:\n",
    "class Person:\n",
    "  def __init__(self, name, age):\n",
    "    self.name = name\n",
    "    self.age = age\n",
    "\n",
    "#create a method in person class\n",
    "  def myfunc(self):\n",
    "    print(\"Hello my name is \" + self.name)\n",
    "obj2 = Person(\"John\", 36)\n",
    "\n",
    "print(obj2.name)\n",
    "print(obj2.age)\n",
    "obj2.myfunc()\n",
    "\n",
    "#modify object properties\n",
    "obj2.age = 40\n",
    "\n",
    "#delete properties on objects by using the del keyword\n",
    "del obj2.age\n",
    "\n",
    "#delete an object by using the del keyword\n",
    "del obj2\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Python iterators"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "An iterator is an object that contains a countable number of values. In Python, an iterator is an object which implements the iterator protocol, which consist of the methods __iter__() and __next__().\n",
    "\n",
    "Lists, tuples, dictionaries, and sets are all iterable objects. They are iterable containers which you can get an iterator from. All these objects have a iter() method which is used to get an iterator. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#return an iterator from a tuple, and print each value\n",
    "mytuple = (\"apple\", \"banana\", \"cherry\")\n",
    "myit = iter(mytuple)\n",
    "\n",
    "print(next(myit))\n",
    "\n",
    "#strings are iterable objects\n",
    "mystr = \"banana\"\n",
    "myit = iter(mystr)\n",
    "\n",
    "print(next(myit))\n",
    "\n",
    "#we can use for to iterate through the above tuple or string\n",
    "#The for loop actually creates an iterator object and executes the next() method for each loop.\n",
    "for x in mytuple:\n",
    "  print(x)\n",
    "\n",
    "for x in mystr:\n",
    "  print(x)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To create an object/class as an iterator you have to implement the methods __iter__() and __next__() to your object. As you have learned in the Python Classes/Objects section, all classes have a function called __init__(), which allows you do some initializing when the object is being created. The __iter__() method acts similar, you can do operations (initializing etc.), but must always return the iterator object itself. The __next__() method also allows you to do operations, and must return the next item in the sequence. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#create an iterator that returns numbers, starting with 1,\n",
    "#and each sequence will increase by one (returning 1,2,3,4,5 etc.).\n",
    "class MyNumbers:\n",
    "   def __iter__(self):\n",
    "    self.a = 1\n",
    "    return self\n",
    "\n",
    "   def __next__(self):\n",
    "    x = self.a\n",
    "    self.a += 1\n",
    "    return x\n",
    "\n",
    "myclass = MyNumbers()\n",
    "myiter = iter(myclass)\n",
    "\n",
    "print(next(myiter))\n",
    "print(next(myiter)) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The example above would continue forever if you had enough next() statements, or if it was used in a for loop. To prevent the iteration to go on forever, we can use the StopIteration statement. In the __next__() method, we can add a terminating condition to raise an error if the iteration is done a specified number of times. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#stop after 20 iterations\n",
    "class MyNumbers:\n",
    "   def __iter__(self):\n",
    "     self.a = 1\n",
    "     return self\n",
    "\n",
    "   def __next__(self):\n",
    "     if self.a <= 20:\n",
    "       x = self.a\n",
    "       self.a += 1\n",
    "       return x\n",
    "     else:\n",
    "       raise StopIteration\n",
    "\n",
    "myclass = MyNumbers()\n",
    "myiter = iter(myclass)\n",
    "\n",
    "for x in myiter:\n",
    "  print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Python JSON"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "JSON (JavaScript object notation) is a syntax for storing and exchanging data. Python has a built-in package called json, which can be use to work with JSON data. Python supports convertion between JSON and Python. e.g. To convert from a JSON to Python object, use json.loads() method. The result will be a Python dictionary. To convert from Python object to a JSON string, use json.dumps() method. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#convert from JSON to Python\n",
    "import json\n",
    "\n",
    "# some JSON:\n",
    "x = '{ \"name\":\"John\", \"age\":30, \"city\":\"New York\"}'\n",
    "\n",
    "# parse x:\n",
    "y = json.loads(x)\n",
    "\n",
    "# the result is a Python dictionary:\n",
    "print(y[\"age\"])\n",
    "\n",
    "#convert from Python to JSON\n",
    "import json\n",
    "\n",
    "# a Python object (dict):\n",
    "x = {\n",
    "   \"name\": \"John\",\n",
    "   \"age\": 30,\n",
    "   \"city\": \"New York\"\n",
    "}\n",
    "\n",
    "# convert into JSON:\n",
    "y = json.dumps(x)\n",
    "\n",
    "# the result is a JSON string:\n",
    "print(y)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can convert Python objects of the following types, into JSON strings: dict, list, tuple, string, int, float, True, False and None into Object, array, array, string, number, number, true, false, and null. The json.dumps() method has parameters to make the converted JSON string easier to read. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#use the indent parameter to define the numbers of indents\n",
    "json.dumps(x, indent=4)\n",
    "\n",
    "#use the separators parameter change the default separator\n",
    "#the separator default value is (\", \", \": \"),\n",
    "#which means using a comma and a space to separate each object,\n",
    "#and a colon and a space to separate keys from values.\n",
    "json.dumps(x, indent=4, separators=(\". \", \" = \"))\n",
    "\n",
    "#use the sort_keys parameter to specify if the result should be sorted or not\n",
    "json.dumps(x, indent=4, sort_keys=True)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Python RegEx"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A RegEx, or Regular Expression, is a sequence of characters that forms a search pattern. RegEx can be used to check if a string contains the specified search pattern. Python has a built-in package called \"re\" to work with Regular Expressions. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import re\n",
    "\n",
    "txt = \"The rain in Spain\"\n",
    "x = re.search(\"^The.*Spain$\", txt) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./images/p02/regex-functions.jpg\" width=\"60%\" and height=\"60%\"> \n",
    "\n",
    "<img src=\"./images/p02/metacharacters.jpg\" width=\"60%\" and height=\"60%\"> \n",
    "\n",
    "<img src=\"./images/p02/special-sequences.jpg\" width=\"60%\" and height=\"60%\"> \n",
    "\n",
    "<img src=\"./images/p02/sets.jpg\" width=\"60%\" and height=\"60%\"> \n",
    "\n",
    "<!--<img src=\"./images/p02/xxx.jpg\" width=\"60%\" and height=\"60%\"> -->\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Examples: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#findall() function returns a list containing all matches\n",
    "#If no matches are found, an empty list is returned\n",
    "import re\n",
    "\n",
    "str = \"Deakin University\"\n",
    "x = re.findall(\"i\", str)\n",
    "print(x)\n",
    "\n",
    "#search() function searches the string for a match,\n",
    "#and returns a Match object if there is a match.\n",
    "#if there is more than one match, only the first occurrence of the match will be returned\n",
    "#if no matches are found, the value None is returned\n",
    "str = \"Deakin University\"\n",
    "x = re.search(\"\\s\", str)\n",
    "\n",
    "print(\"The first white-space character is located in position:\", x.start())\n",
    "\n",
    "#split() function returns a list where the string has been split at each match\n",
    "str = \"Deakin University\"\n",
    "x = re.split(\"\\s\", str)\n",
    "print(x)\n",
    "\n",
    "#control the number of occurrences by specifying the maxsplit parameter\n",
    "#split the string only at the first occurrence\n",
    "str = \"Deakin University is in Victoria\"\n",
    "x = re.split(\"\\s\", str, 1)\n",
    "print(x)\n",
    "\n",
    "#sub() function replaces the matches with the text of your choice\n",
    "#replace every white-space character with the number 9\n",
    "str = \"Deakin University is in Victoria\"\n",
    "x = re.sub(\"\\s\", \"9\", str)\n",
    "print(x)\n",
    "\n",
    "#control the number of replacements by specifying the count parameter\n",
    "#replace the first 2 occurrences\n",
    "x = re.sub(\"\\s\", \"9\", str, 2)\n",
    "print(x) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Match object"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A Match Object is an object containing information about the search and the result. If no match, a value None will be returned, instead of the match object.\n",
    "\n",
    "The Match object has properties and methods used to retrieve information about the search, and the result:\n",
    "\n",
    "  - span() returns a tuple containing the start-, and end positions of the match.\n",
    "  - string returns the string passed into the function\n",
    "  - group() returns the part of the string where there was a match\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#do a search that will return a Match Object\n",
    "import re\n",
    "\n",
    "str = \"Deakin University is in Victoria\"\n",
    "x = re.search(\"ea\", str)\n",
    "print(x) #this will print an object\n",
    "\n",
    "#print the position (start- and end-position) of the first match occurrence.\n",
    "#look for any words that starts with an upper case \"U\"\n",
    "x = re.search(r\"\\bU\\w+\", str)\n",
    "print(x.span())\n",
    "\n",
    "#print the string passed into the function\n",
    "print(x.string)\n",
    "\n",
    "#print the part of the string where there was a match\n",
    "print(x.group())\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Python file handling"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "File handling is an important part of any data processing application. Python has several functions for creating, reading, updating, and deleting files.\n",
    "\n",
    "The key function for working with files in Python is the open() function. The open() function takes two parameters: filename, and mode. There are four different methods (modes) for opening a file:\n",
    "\n",
    "  -  \"r\" - Read - Default value. Opens a file for reading, error if the file does not exist\n",
    "  -  \"a\" - Append - Opens a file for appending, creates the file if it does not exist\n",
    "  -  \"w\" - Write - Opens a file for writing, creates the file if it does not exist\n",
    "  -  \"x\" - Create - Creates the specified file, returns an error if the file exists\n",
    "\n",
    "In addition you can specify if the file should be handled as binary or text mode\n",
    "\n",
    "   - \"t\" - Text - Default value. Text mode\n",
    "   - \"b\" - Binary - Binary mode (e.g. images)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "#open a file for reading it is enough to specify the name of the file\n",
    "#the same as f = open(\"demofile.txt\", \"rt\") because \"r\" for read, \n",
    "#and \"t\" for text are the default values\n",
    "#make sure the file exists, or else you will get an error.\n",
    "\n",
    "f = open(\"./data/p02/demofile.txt\") \n",
    "\n",
    "#assume we have the following file in the folder as specified or \n",
    "#you update the above path to reflect its actual location on your computer \n",
    "#demofile.txt\n",
    "#Hello, welcome to SIT384.\n",
    "#This file is for demonstration purposes only.\n",
    "\n",
    "#open the file, use open(); use read() method to read the content\n",
    "\n",
    "f = open(\"./data/p02/demofile.txt\", \"r\")\n",
    "print(f.read())\n",
    "\n",
    "#by default the read() method returns the whole text,\n",
    "#but you can also specify how many character you want to return.\n",
    "print(f.read(10))\n",
    "\n",
    "#return one line by using the readline() method\n",
    "print(f.readline())\n",
    "\n",
    "#looping through the lines of the file, you can read the whole file, line by line\n",
    "for x in f:\n",
    "  print(x) \n",
    "\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " To write to an existing file, you must add a parameter to the open() function:\n",
    "\n",
    "   - \"a\" - Append - will append to the end of the file\n",
    "   - \"w\" - Write - will overwrite any existing content\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#open the file \"demofile.txt\" and append content to the file\n",
    "f = open(\"./data/p02/demofile.txt\", \"a\")\n",
    "f.write(\"Now the file has one more line!\")\n",
    "\n",
    "#open the file \"demofile.txt\" and overwrite the content\n",
    "f = open(\"./data/p02/demofile.txt\", \"w\")\n",
    "f.write(\"Woops! I have deleted the content!\") \n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " To create a new file in Python, use the open() method, with one of the following parameters:\n",
    "\n",
    "  -  \"x\" - Create - will create a file, returns an error if the file exist\n",
    "  -  \"a\" - Append - will create a file if the specified file does not exist\n",
    "  -  \"w\" - Write - will create a file if the specified file does not exist\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#create a file called \"myfile.txt\":\n",
    "f = open(\"./data/p02/myfile.txt\", \"x\") # a new empty file is created\n",
    "\n",
    "#create a new file if it does not exist:\n",
    "f = open(\"./data/p02/myfile.txt\", \"w\")\n",
    "\n",
    "#to delete a file, you must import the OS module, and run its os.remove() function\n",
    "import os\n",
    "os.remove(\"./data/p02/demofile.txt\")\n",
    "\n",
    "#to avoid getting an error, you might want to check if the file exist before you try to delete it\n",
    "if os.path.exists(\"./data/p02/demofile.txt\"):\n",
    "  os.remove(\"./data/p02/demofile.txt\")\n",
    "else:\n",
    "   print(\"The file does not exist\")\n",
    "\n",
    "#to delete an entire folder, use the os.rmdir() method\n",
    "import os\n",
    "os.rmdir(\"./data/p02/myfolder\") #you can only remove empty folders, receive error if folder doesn't exist.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All right, I will stop here. It looks tedious, but it should not take too long to read. More Python information and examples can be found at [Python examples](https://www.w3schools.com/python/python_examples.asp)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tasks\n",
    "\n",
    "Try the provided examples and get yourself familiar with Python syntax before attempting portolio tasks.\n",
    "\n",
    "Please show your attempt to your tutor before you leave the lab, or email your files to your coordinator if you are a cloud student.\n",
    "\n",
    "## Summary\n",
    "\n",
    "In this session we have covered\n",
    "\n",
    "  -  Python syntax, such as functions, classes, RegEx and file handling\n",
    "  -  Coding Python programs using the introduced concepts\n",
    "\n",
    "\n",
    "## Reference:\n",
    "\n",
    "   - Python tutorial, available at <https://www.w3schools.com/python/default.asp>, accessed 15th of March, 2020.\n",
    "   - Python Software Foundation, available at <https://www.python.org/>, accessed 15th of March, 2020.\n",
    "\n",
    " \n"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "SIT742P01A-Python.ipynb",
   "provenance": [],
   "version": "0.3.2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
